﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
using Maria.Common.Aspects;
using RG.Common.Configuration;
using RG.Common.Export;
using RG.Common.WPF.Filters;
using RG.Common.WPF.Workplaces;
using Microsoft.Practices.Unity;
using RG.Common;
using RG.Common.WPF;

namespace RG.Common.WPF.BaseFrame
{
    [SupportDataBinding]
    public abstract class BaseListFrameVM<TFrame, Tdto, Tvm, TSrv, TParam> : IFrameForMultiWorkplace
        where TFrame : BaseListFrameVM<TFrame, Tdto, Tvm, TSrv, TParam>
        where TSrv : IHugeDataService<Tdto, TParam>
        where TParam: IParam, new()
    {
        [Dependency]
        public IUnityContainer Cfg { get; set; }

        [Dependency]
        public WorkplaceWorker Worker { get; set; }

        [Dependency]
        public IWorkplace Workplace { get; set; }

        [Dependency]
        public TSrv Service { get; set; }

        #region bindings to frame

        public ToolBarButtonsVM ToolBar { get; set; }

        [CollectSettings]
        public FiltersVM Filter { get; set; }

        public GridVM<Tvm> Grid { get; set; }

        public string StateMsg { get; set; }

        public ICommand RefreshCmd { get; set; }

        #endregion

        protected BaseListFrameVM(IUnityContainer cfg)
        {
            cfg.BuildUp((TFrame)this);

            ToolBar = new ToolBarButtonsVM { Items = new ObservableCollection<IToolbarButton>() };
            InitButtons(ToolBar.Items);

            Grid = new GridVM<Tvm>
                       {
                           Items = new ObservableCollection<Tvm>(),
                           Columns = new ObservableCollection<IColumnInfo>(),
                           ItemsChanged = ItemsChanged,
                           SelectedItemChanged = SelectedItemChanged
                       };
            cfg.BuildUp(Grid);
            InitColumns(Grid.Columns);

            Filter = cfg.Resolve<FiltersVM>();
            InitFilters(Filter.Filter);
            Filter.Load(Filter.Filter);
            Filter.HideExpander = false;

            var RefreshBtn = ToolBar.Items.FirstOrDefault(x => x is RefreshButton);
            if (RefreshBtn != null) RefreshCmd = RefreshBtn.Command;
        }

        /// <summary>
        /// Перегружаем чтобы заполнить кнопки
        /// </summary>
        protected virtual void InitButtons(ObservableCollection<IToolbarButton> buttons) { }

        /// <summary>
        /// Перегружаем чтобы заполнить колонки в гриде
        /// </summary>
        protected abstract void InitColumns(ObservableCollection<IColumnInfo> columns);

        /// <summary>
        /// Определяем для заполения фильтров
        /// </summary>
        protected virtual void InitFilters(ObservableCollection<FilterBaseVM> filters) { }
        
        private List<Tvm> SingleLoadItems()
        {
            return Service.List(InitQueryParam()).Select(fromDTO).ToList();
        }

        private List<Tvm> HugeLoadItems()
        {
            var result = new List<Tdto>();
            var param = InitQueryParam();

            StateMsg = string.Format("Загрузка ...");
            
            var ids = Service.ListPrepare(param);

            foreach (var chunk in ids.Chunk(50))
            {
                StateMsg = string.Format("Получаем {0} из {1}", result.Count, ids.Count);

                var newParam = new TParam {Ids = chunk.ToList<int>()};
                var data = Service.List(newParam);
                result.AddRange(data);
            }
            StateMsg = string.Format("Загрузили {0}", result.Count);
            return result.Select(fromDTO).ToList();
        }

        /// <summary>
        /// Перегружаем этот метод для заполнения параметров запроса из фильтров
        /// </summary>
        protected abstract TParam InitQueryParam();
        
        /// <summary>
        /// Перегружаем метод при необходимости что нибудь сделать после вызова функции Refresh
        /// </summary>
        protected virtual void AfterRefresh(List<Tvm> result)
        {
            Items = new ObservableCollection<Tvm>(result);
        }

        protected virtual bool BeforeRefresh() { return true; }

        public ObservableCollection<Tvm> Items
        {
            get { return Grid.Items; }
            set { Grid.Items = value;}
        }

        protected ObservableCollection<IColumnInfo> Columns
        {
            get { return Grid.Columns; }
            set { Grid.Columns = value; }
        }

        protected virtual void Refresh()
        {
            HugeRefresh(); // Можно переопределить на SingleRefresh, если потребуется
        }

        /// <summary>
        /// Метод загружает элементы последовательно из сервиса в разных транзакциях
        /// </summary>
        protected void HugeRefresh()
        {
            if (!BeforeRefresh()) return;
            Worker.Do(HugeLoadItems, AfterRefresh);
        }

        /// <summary>
        /// Метод загружает элементы в одной транзакции
        /// </summary>
        protected void SingleRefresh()
        {
            if (!BeforeRefresh()) return;
            Worker.Do(SingleLoadItems, AfterRefresh);
        }

        /// <summary>
        /// Перегружаем для отслеживания изменений в коллекции элементов
        /// </summary>
        protected virtual void ItemsChanged() { }

        /// <summary>
        /// Перегружаем для отслеживания изменения выбранного элемента
        /// </summary>
        protected virtual void SelectedItemChanged() { }

        /// <summary>
        /// Перегружаем для преобразования DTO к VM
        /// </summary>
        protected abstract Tvm fromDTO(Tdto dto);
        
        protected Tvm SelectedItem
        {
            get { return Grid.SelectedItem; }
            set { Grid.SelectedItem = value; }
        }

        public virtual bool CanClose { get { return true; } }
        public string Name { get; set; }
    }
}
